package com.oj.controller;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.oj.config.OJConfig;
import com.oj.entity.*;
import com.oj.mapper.CompileInfoMapper;
import com.oj.mapper.ContestProblemMapper;
import com.oj.mapper.SolutionMapper;
import com.oj.service.*;
import com.oj.service.impl.I18nService;
import com.oj.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Controller
@RequestMapping
public class SolutionController {
    @Autowired
    private OJConfig ojConfig;

    @Autowired
    private I18nService i18nService;

    @Autowired
    private SolutionMapper solutionMapper;

    @Autowired
    private SolutionService solutionService;

    @Autowired
    private ProblemService problemService;

    @Autowired
    private UserService userService;

    @Autowired
    private SolutionSourceService solutionSourceService;

    @Autowired
    private ContestService contestService;

    @Autowired
    private ContestProblemMapper contestProblemMapper;
    @Autowired
    private CompileInfoMapper compileInfoMapper;

    @RequestMapping(value = {
            "/problemset/status",
            "/problemset/status/page/{pageNum}",
            "/problemset/{problemId}/status/page/{pageNum}",
            "/problemset/status/{username}/page/{pageNum}",
            "/problemset/{problemId}/status/{username}/page/{pageNum}"}, method = RequestMethod.GET)
    public ModelAndView solutions(@PathVariable(value = "pageNum",required = false)Integer pageNum,
                                  @PathVariable(value = "problemId",required = false)Integer problemId,
                                  @RequestParam(value = "pageSize",required = false, defaultValue = "50")Integer pageSize,
                                  @PathVariable(value = "username",required = false)String username){
        if (pageNum == null) {
            pageNum = 1;
        }
        PageHelper.startPage(pageNum,pageSize);
        Map param = new HashMap();
        param.put("problemId", problemId);
        param.put("username", username);
        Page<Solution> solutions = solutionMapper.findByPaging(param);
        for (Solution s:solutions.getResult()) {
            s.setUser(userService.query(s.getUsername()));
            Problem problem = problemService.query(s.getProblem_id());
            if (problem != null) {
                s.setProblem(problem);
            } else {
                s.setProblem(new Problem());
            }
            s.setFriendlySubmitDate(DateUtil.toFriendlyDate(s.getSubmit_date()));
            s.setStatus_description(OJUtil.getVerdictName(s.getVerdict(), s.getTestcase()));
            if (s.getLanguage_name() == null) {
                s.setLanguage_name(OJUtil.getLanguageName("GUET", s.getLanguage()));
            }
        }

        PageInfo<Solution> pageInfo = new PageInfo<>(solutions);
        ModelAndView mv = new ModelAndView();
        mv.addObject("pageInfo", pageInfo);
        mv.addObject("username", username);
        mv.addObject("problemId", problemId);
        mv.setViewName("problem/status.html");
        return mv;
    }
    @RequestMapping(value = {"/contest/{contestId}/status",
            "/contest/{contestId}/status/page/{pageNum}",
            "/contest/{contestId}/status/problem/{problemId}/page/{pageNum}",
            "/contest/{contestId}/status/{username}/page/{pageNum}"}, method = RequestMethod.GET)
    public ModelAndView contestSolutions(@PathVariable(value = "pageNum",required = false)Integer pageNum,
                                         @PathVariable(value = "contestId",required = true)Integer contestId,
                                         @PathVariable(value = "problemId",required = false)String problemId,
                                         @RequestParam(value = "pageSize",required = false, defaultValue = "50")Integer pageSize,
                                         @PathVariable(value = "username",required = false)String username){
        if (pageNum == null) {
            pageNum = 1;
        }
        Contest contest = contestService.query(contestId);
        if (contest == null) {
            return null;
        }
        Integer problemId_ = null;
        if (problemId != null) {
            ContestProblem cp = contestProblemMapper.query(contestId, problemId);
            if (cp != null) {
                problemId_ = cp.getProblem_id();
            }
        }
        PageHelper.startPage(pageNum,pageSize);
        Map param = new HashMap();
        param.put("contestId", contestId);
        param.put("problemId", problemId_);
        param.put("username", username);
        Page<Solution> solutions = solutionMapper.findByPaging(param);
        for (Solution s:solutions.getResult()) {
            s.setUser(userService.query(s.getUsername()));
            s.setProblem(problemService.queryAdmin(s.getProblem_id()));
            s.setContestProblem(contestProblemMapper.queryByProblemId(s.getContest_id(), s.getProblem_id()));
            s.setFriendlySubmitDate(DateUtil.toFriendlyDate(s.getSubmit_date()));
            s.setStatus_description(OJUtil.getVerdictName(s.getVerdict(), s.getTestcase()));
            if (s.getLanguage_name() == null) {
                s.setLanguage_name(OJUtil.getLanguageName("GUET", s.getLanguage()));
            }
        }

        PageInfo<Solution> pageInfo = new PageInfo<>(solutions);
        ModelAndView mv = new ModelAndView();
        mv.addObject("pageInfo", pageInfo);
        mv.addObject("username", username);
        mv.addObject("problemId", problemId);
        mv.addObject("contest", contest);
        mv.setViewName("contest/status.html");
        return mv;
    }
    @ResponseBody
    @RequestMapping(value = {"/api/submission/query"}, method = RequestMethod.GET)
    public Result<List> submissions(@RequestParam Map<String, Object> map, HttpServletRequest request){
        String problemIdStr = null;
        Integer problemId = null;
        if (map.get("problemId") != null && !"".equals(map.get("problemId").toString())) {
            problemIdStr = map.get("problemId").toString();
        }
        Contest contest = null;
        Integer contestId = null;
        if (map.get("contestId") != null && !"0".equals(map.get("contestId").toString())) {
            contestId = Integer.parseInt(map.get("contestId").toString());
            contest = contestService.query(contestId);
            if (contest == null) {
                return Result.error(CodeMsg.PARAM_INVALID.locale());
            }
            if (problemIdStr != null) {
                ContestProblem cp = contestProblemMapper.query(contestId, problemIdStr);
                problemId = cp.getProblem_id();
            }
        } else {
            if (problemIdStr != null) {
                problemId = Integer.parseInt(problemIdStr);
            }
        }

        Integer fromSolutionId = null;
        if (map.get("fromSolutionId") != null && !"".equals(map.get("fromSolutionId").toString())) {
            fromSolutionId = Integer.parseInt(map.get("fromSolutionId").toString());
        }
        List<Solution> solutions = solutionMapper.queryList(map.get("username").toString(), problemId, contestId, fromSolutionId);
        for (Solution s:solutions) {
            s.setUser(userService.query(s.getUsername()));
            s.setProblem(problemService.queryAdmin(s.getProblem_id()));
            s.setFriendlySubmitDate(DateUtil.toFriendlyDate(s.getSubmit_date()));

            s.setStatus_description(OJUtil.getVerdictName(s.getVerdict(), s.getTestcase()));
            if (s.getLanguage_name() == null) {
                s.setLanguage_name(OJUtil.getLanguageName("GUET", s.getLanguage()));
            }

            if (contest != null) {
                s.setTimeSinceContestStart(DateUtil.penaltyString((int)((s.getSubmit_date().getTime()-contest.getStart_time().getTime())/1000)));
                ContestProblem cp = new ContestProblem();
                cp = contestProblemMapper.queryByProblemId(contestId, s.getProblem_id());
                if(cp != null) {
                    s.setProblemNum(cp.getNum());
                }
            }
        }
        return Result.success(solutions);
    }

    @ResponseBody
    @RequestMapping(value = {"/api/status/query"})
    public Result<List> status(@RequestParam Map<String, Object> map){
        List<Solution> solutions = new ArrayList<Solution>();
        try {
            String[] id = map.get("ids").toString().split(",");
            for (String i : id) {
                Solution solution = solutionService.query(Integer.parseInt(i));
                if (solution != null) {
                    solution.setStatus_description(OJUtil.getVerdictName(solution.getVerdict(), solution.getTestcase()));
                    if (solution.getLanguage_name() == null) {
                        solution.setLanguage_name(OJUtil.getLanguageName("GUET", solution.getLanguage()));
                    }
                    solutions.add(solution);
                }
            }
        } catch (Exception e) {
        }
        return Result.success(solutions);
    }

    @ResponseBody
    @RequestMapping(value = {"/api/solution/query"}, method = RequestMethod.GET)
    public Result<Solution> solution(@RequestParam Map<String, Object> map, HttpServletRequest request){
        Integer solutionId = null;
        if (map.get("solutionId") != null && !"".equals(map.get("solutionId").toString())) {
            solutionId = Integer.parseInt(map.get("solutionId").toString());
        }
        Solution s = solutionService.query(solutionId);
        if (s == null) {
            return Result.error(CodeMsg.PARAM_INVALID.locale());
        }

        User user = userService.query(s.getUsername());
        if (user == null) {
            return Result.error(CodeMsg.PARAM_INVALID.locale());
        }
        User userMe = userService.getUserBySession(request);
        if (userMe == null || !userMe.getUsername().equals(user.getUsername())) {
            if (!ojConfig.getOpenSource()) {
                return Result.error(CodeMsg.OPENSOURCE_IS_CLOSE.locale());
            }
            if(user.getOpensource().equals("N")){
                return Result.error(CodeMsg.USER_OPENSOURCE_IS_CLOSE.locale());
            }
            if (s.getContest_id() != 0) {
                if (!contestService.isContestEnded(s.getContest_id())) {
                    if (userMe == null || !userMe.getUsername().equals(user.getUsername())) {
                        return Result.error(CodeMsg.VIEW_SOURCE_CONTEST_IS_RUNNING.locale());
                    }
                }
            }
        }

        if (s.getContest_id() != 0) {
            ContestProblem cp = contestProblemMapper.queryByProblemId(s.getContest_id(), s.getProblem_id());
            if (cp != null) {
                s.setContestProblem(cp);
            }
        }
        s.setUser(user);
        s.setProblem(problemService.queryAdmin(s.getProblem_id()));
        s.setSolutionSource(solutionSourceService.query(s.getSolution_id()));
        s.getSolutionSource().setSource(s.getSolutionSource().getSource().replaceAll("<", "&lt;").replaceAll(">", "&gt;"));
        s.setFriendlySubmitDate(DateUtil.toFriendlyDate(s.getSubmit_date()));
        s.setStatus_description(i18nService.getMessage("verdict" + s.getVerdict()));
        if (s.getLanguage_name() == null) {
            s.setLanguage_name(OJUtil.getLanguageName("GUET", s.getLanguage()));
        }

        /* judge-log */
        if (s.getVerdict() == 3) {
            CompileInfo ce = compileInfoMapper.query(s.getSolution_id());
            if (ce != null) {
                s.setJudgeLog(ce.getError());
            }
        } else {
            File file = new File(OJConfig.getJudgeLogPath() + "judge-log-" + solutionId + ".log");
            s.setJudgeLog(StreamHandler.readEx(file));
        }

        return Result.success(s);
    }

    @ResponseBody
    @RequestMapping(value = {"/api/solution/test"}, method = RequestMethod.GET)
    public Result<PageInfo> findByPaging(@RequestParam Map<String, Object> map){
        Integer pageNum = 1;
        if (map.get("pageNum") != null && !"".equals(map.get("pageNum").toString())) {
            pageNum = Integer.parseInt(map.get("pageNum").toString());
        }
        Integer pageSize = 50;
        if (map.get("pageSize") != null && !"".equals(map.get("pageSize").toString())) {
            pageSize = Integer.parseInt(map.get("pageSize").toString());
        }
        String username = null;
        if (map.get("username") != null && !"".equals(map.get("username").toString())) {
            username = map.get("username").toString();
        }
        PageHelper.startPage(pageNum,pageSize);
        Map param = new HashMap();
        param.put("username", username);
        Page<Solution> data = solutionMapper.findByPaging(param);
        PageInfo<Solution> pageInfo = new PageInfo<>(data);
        return Result.success(pageInfo);
    }

    @ResponseBody
    @RequestMapping(value = {"/api/solution/try"}, method = RequestMethod.GET)
    public Result<List> userProblemTry(@RequestParam Map<String, Object> map){
        List<Integer> problemIds = new ArrayList<Integer>();
        List<Map> problems = new ArrayList<Map>();
        try {
            String username = null;
            if (map.get("username") != null && !"".equals(map.get("username").toString())) {
                username = map.get("username").toString();
            }
            String type = null;
            if (map.get("type") != null && !"".equals(map.get("type").toString())) {
                type = map.get("type").toString();
            }
            Map param = new HashMap();
            param.put("username", username);
            param.put("type", type);
            problemIds = solutionMapper.queryUserProblem(param);
            for (Integer id: problemIds) {
                Problem problem = problemService.queryAdmin(id);
                if (problem != null) {
                    Map m = new HashMap();
                    m.put("problemId", problem.getProblem_id());
                    m.put("title", problem.getTitle());
                    problems.add(m);
                }
            }
        } catch (Exception e) {
        }
        return Result.success(problems);
    }
}